Skip to content

Commit f244a83

Browse files
added Access form/report support to GetControlType
added GetDocumentClassControlType to VBETypeLibsAPI for getting control types from Access forms/reports added VBETypeLibsAPI.IsAccessForm added VBETypeLibsAPI.IsAccessReport renamed GetStdModInstance to GetStdModAccessor
1 parent 13a483a commit f244a83

File tree

3 files changed

+216
-19
lines changed

3 files changed

+216
-19
lines changed

Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeLibs.cs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,8 @@
1212

1313

1414
// TODO comments/XML doc
15-
// TODO a few FIXMEs
16-
17-
// make GetControlType support Access forms etc
18-
// IsAccessForm example
19-
// split into TypeInfos.cs
20-
21-
/*VBETypeLibsAPI::GetModuleFlags(ide, projectName, moduleName) to get the TYPEFLAGS
22-
VBETypeLibsAPI::GetMemberId(ide, projectName, moduleName, memberName)
23-
VBETypeLibsAPI::GetMemberHelpString(ide, projectName, moduleName, memberName)
24-
*/
15+
// TODO add memory address validation in ReadStructureSafe
16+
// TODO split into TypeInfos.cs
2517

2618

2719
// USAGE GUIDE: see class VBETypeLibsAPI for demonstrations of usage.
@@ -94,8 +86,6 @@ public static T ReadStructure<T>(IntPtr memAddress)
9486
public static T ReadStructureSafe<T>(IntPtr memAddress)
9587
{
9688
if (memAddress == IntPtr.Zero) return default(T);
97-
98-
// FIXME add memory address validation here, using VirtualQueryEx
9989
return (T)Marshal.PtrToStructure(memAddress, typeof(T));
10090
}
10191
}
@@ -717,23 +707,23 @@ private void DetectUserFormClass()
717707
}
718708

719709
// caller is responsible for calling ReleaseComObject
720-
public IDispatch GetStdModInstance()
710+
public IDispatch GetStdModAccessor()
721711
{
722712
if (HasVBEExtensions)
723713
{
724-
return target_IVBETypeInfo.GetStdModInstance();
714+
return target_IVBETypeInfo.GetStdModAccessor();
725715
}
726716
else
727717
{
728-
throw new ArgumentException("This ITypeInfo is not hosted by the VBE, so does not support GetStdModInstance");
718+
throw new ArgumentException("This ITypeInfo is not hosted by the VBE, so does not support GetStdModAccessor");
729719
}
730720
}
731721

732722
public object StdModExecute(string name, Reflection.BindingFlags invokeAttr, object[] args = null)
733723
{
734724
if (HasVBEExtensions)
735725
{
736-
var StaticModule = GetStdModInstance();
726+
var StaticModule = GetStdModAccessor();
737727
var retVal = StaticModule.GetType().InvokeMember(name, invokeAttr, null, StaticModule, args);
738728
Marshal.ReleaseComObject(StaticModule);
739729
return retVal;
@@ -753,6 +743,10 @@ public TypeInfoWrapper GetControlType(string controlName)
753743
{
754744
using (func)
755745
{
746+
// Controls are exposed as getters on the interface.
747+
// can either be ControlType* get_ControlName()
748+
// or HRESULT get_ControlName(ControlType** Out)
749+
756750
if ((func.Name == controlName) &&
757751
(func.ProcKind == TypeInfoFunc.PROCKIND.PROCKIND_GET) &&
758752
(func.ParamCount == 0) &&
@@ -764,6 +758,27 @@ public TypeInfoWrapper GetControlType(string controlName)
764758
return GetSafeRefTypeInfo((int)retValElement.tdesc.lpValue);
765759
}
766760
}
761+
else if ((func.Name == controlName) &&
762+
(func.ProcKind == TypeInfoFunc.PROCKIND.PROCKIND_GET) &&
763+
(func.ParamCount == 1) &&
764+
(func.FuncDesc.elemdescFunc.tdesc.vt == (short)VarEnum.VT_HRESULT))
765+
{
766+
// Get details of the first argument
767+
var retValElementOuterPtr = StructHelper.ReadStructure<ComTypes.ELEMDESC>(func.FuncDesc.lprgelemdescParam);
768+
if (retValElementOuterPtr.tdesc.vt == (short)VarEnum.VT_PTR)
769+
{
770+
var retValElementInnerPtr = StructHelper.ReadStructure<ComTypes.ELEMDESC>(retValElementOuterPtr.tdesc.lpValue);
771+
if (retValElementInnerPtr.tdesc.vt == (short)VarEnum.VT_PTR)
772+
{
773+
var retValElement = StructHelper.ReadStructure<ComTypes.ELEMDESC>(retValElementInnerPtr.tdesc.lpValue);
774+
775+
if (retValElement.tdesc.vt == (short)VarEnum.VT_USERDEFINED)
776+
{
777+
return GetSafeRefTypeInfo((int)retValElement.tdesc.lpValue);
778+
}
779+
}
780+
}
781+
}
767782
}
768783
}
769784

Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeLibsAPI.cs

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,18 @@ public void SetProjectConditionalCompilationArgsRaw(string projectName, string n
2828
=> VBETypeLibsAPI.SetProjectConditionalCompilationArgsRaw(_ide, projectName, newConditionalArgs);
2929
public bool DoesClassImplementInterface(string projectName, string className, string interfaceProgId)
3030
=> VBETypeLibsAPI.DoesClassImplementInterface(_ide, projectName, className, interfaceProgId);
31-
public string GetUserFormControlType(string projectName, string userFormName, string controlName)
31+
public string GetUserFormControlType(string projectName, string userFormName, string controlName)
3232
=> VBETypeLibsAPI.GetUserFormControlType(_ide, projectName, userFormName, controlName);
33+
public string GetDocumentClassControlType(string projectName, string documentClassName, string controlName)
34+
=> VBETypeLibsAPI.GetDocumentClassControlType(_ide, projectName, documentClassName, controlName);
35+
public bool IsExcelWorksheet(string projectName, string className)
36+
=> VBETypeLibsAPI.IsExcelWorksheet(_ide, projectName, className);
37+
public bool IsExcelWorkbook(string projectName, string className)
38+
=> VBETypeLibsAPI.IsExcelWorkbook(_ide, projectName, className);
39+
public bool IsAccessForm(string projectName, string className)
40+
=> VBETypeLibsAPI.IsAccessForm(_ide, projectName, className);
41+
public bool IsAccessReport(string projectName, string className)
42+
=> VBETypeLibsAPI.IsAccessReport(_ide, projectName, className);
3343
public string DocumentAll()
3444
=> VBETypeLibsAPI.DocumentAll(_ide);
3545
}
@@ -480,6 +490,112 @@ public static bool IsExcelWorksheet(TypeLibWrapper projectTypeLib, string classN
480490
return DoesClassImplementInterface(projectTypeLib, className, "Excel._Worksheet");
481491
}
482492

493+
/// <summary>
494+
/// Determines whether the specified document class is an Excel Workbook
495+
/// </summary>
496+
/// <param name="ide">Safe-com wrapper representing the VBE</param>
497+
/// <param name="projectName">VBA Project name, as declared in the VBE</param>
498+
/// <param name="className">Document class name, as declared in the VBA project</param>
499+
/// <returns>bool indicating whether the class is an Access Form</returns>
500+
public static bool IsAccessForm(IVBE ide, string projectName, string className)
501+
{
502+
using (var typeLibs = new VBETypeLibsAccessor(ide))
503+
{
504+
return IsAccessForm(typeLibs.Get(projectName), className);
505+
}
506+
}
507+
508+
/// <summary>
509+
/// Determines whether the specified document class is an Excel Workbook
510+
/// </summary>
511+
/// <param name="project">Safe-com wrapper representing the VBA project</param>
512+
/// <param name="className">Document class name, as declared in the VBA project</param>
513+
/// <returns>bool indicating whether the class is an Access Form</returns>
514+
public static bool IsAccessForm(IVBProject project, string className)
515+
{
516+
using (var typeLib = TypeLibWrapper.FromVBProject(project))
517+
{
518+
return IsAccessForm(typeLib, className);
519+
}
520+
}
521+
522+
/// <summary>
523+
/// Determines whether the specified document class is an Excel Workbook
524+
/// </summary>
525+
/// <param name="project">Safe-com wrapper representing the VBA component</param>
526+
/// <returns>bool indicating whether the class is an Access Form</returns>
527+
public static bool IsAccessForm(IVBComponent component)
528+
{
529+
return IsAccessForm(component.ParentProject, component.Name);
530+
}
531+
532+
/// <summary>
533+
/// Determines whether the specified document class is an Excel Workbook
534+
/// </summary>
535+
/// <param name="projectTypeLib">Low-level ITypeLib wrapper representing the VBA project</param>
536+
/// <param name="className">Document class name, as declared in the VBA project</param>
537+
/// <returns>bool indicating whether the class is an Access Form</returns>
538+
public static bool IsAccessForm(TypeLibWrapper projectTypeLib, string className)
539+
{
540+
// The interface used by an Access form depends on the version of Access hosting the form
541+
// so we have to check for the current known interface prog-ids
542+
return DoesClassImplementInterface(projectTypeLib, className,
543+
new string[] { "Access._Form", "Access._Form2", "Access._Form3" }, out int matchIndex);
544+
}
545+
546+
/// <summary>
547+
/// Determines whether the specified document class is an Excel Workbook
548+
/// </summary>
549+
/// <param name="ide">Safe-com wrapper representing the VBE</param>
550+
/// <param name="projectName">VBA Project name, as declared in the VBE</param>
551+
/// <param name="className">Document class name, as declared in the VBA project</param>
552+
/// <returns>bool indicating whether the class is an Access Form</returns>
553+
public static bool IsAccessReport(IVBE ide, string projectName, string className)
554+
{
555+
using (var typeLibs = new VBETypeLibsAccessor(ide))
556+
{
557+
return IsAccessReport(typeLibs.Get(projectName), className);
558+
}
559+
}
560+
561+
/// <summary>
562+
/// Determines whether the specified document class is an Excel Workbook
563+
/// </summary>
564+
/// <param name="project">Safe-com wrapper representing the VBA project</param>
565+
/// <param name="className">Document class name, as declared in the VBA project</param>
566+
/// <returns>bool indicating whether the class is an Access Form</returns>
567+
public static bool IsAccessReport(IVBProject project, string className)
568+
{
569+
using (var typeLib = TypeLibWrapper.FromVBProject(project))
570+
{
571+
return IsAccessReport(typeLib, className);
572+
}
573+
}
574+
575+
/// <summary>
576+
/// Determines whether the specified document class is an Excel Workbook
577+
/// </summary>
578+
/// <param name="project">Safe-com wrapper representing the VBA component</param>
579+
/// <returns>bool indicating whether the class is an Access Form</returns>
580+
public static bool IsAccessReport(IVBComponent component)
581+
{
582+
return IsAccessReport(component.ParentProject, component.Name);
583+
}
584+
585+
/// <summary>
586+
/// Determines whether the specified document class is an Excel Workbook
587+
/// </summary>
588+
/// <param name="projectTypeLib">Low-level ITypeLib wrapper representing the VBA project</param>
589+
/// <param name="className">Document class name, as declared in the VBA project</param>
590+
/// <returns>bool indicating whether the class is an Access Form</returns>
591+
public static bool IsAccessReport(TypeLibWrapper projectTypeLib, string className)
592+
{
593+
// The interface used by an Access form depends on the version of Access hosting the form
594+
// so we have to check for the current known interface prog-ids
595+
return DoesClassImplementInterface(projectTypeLib, className,
596+
new string[] { "Access._Report", "Access._Report2", "Access._Report3" }, out int matchIndex);
597+
}
598+
483599
/// <summary>
484600
/// Determines whether the specified VBA class implements a specific interface
485601
/// </summary>
@@ -814,7 +930,72 @@ public static string GetUserFormControlType(TypeInfoWrapper userFormTypeInfo, st
814930
{
815931
return userFormTypeInfo.ImplementedInterfaces.Get("FormItf").GetControlType(controlName).GetProgID();
816932
}
933+
934+
/// <summary>
935+
/// Returns the class progID of a control on a UserForm
936+
/// </summary>
937+
/// <param name="ide">Safe-com wrapper representing the VBE</param>
938+
/// <param name="projectName">VBA Project name, as declared in the VBE</param>
939+
/// <param name="documentClassName">Document class name, as declared in the VBA project</param>
940+
/// <param name="controlName">Control name, as declared on the UserForm</param>
941+
/// <returns>string class progID of the specified control on a UserForm, e.g. "MSForms.CommandButton"</returns>
942+
public static string GetDocumentClassControlType(IVBE ide, string projectName, string documentClassName, string controlName)
943+
{
944+
using (var typeLibs = new VBETypeLibsAccessor(ide))
945+
{
946+
return GetDocumentClassControlType(typeLibs.Get(projectName), documentClassName, controlName);
947+
}
948+
}
949+
950+
/// <summary>
951+
/// Returns the class progID of a control on a UserForm
952+
/// </summary>
953+
/// <param name="project">Safe-com wrapper representing the VBA project</param>
954+
/// <param name="documentClassName">Document class name, as declared in the VBA project</param>
955+
/// <param name="controlName">Control name, as declared on the UserForm</param>
956+
/// <returns>string class progID of the specified control on a UserForm, e.g. "MSForms.CommandButton"</returns>
957+
public static string GetDocumentClassControlType(IVBProject project, string documentClassName, string controlName)
958+
{
959+
using (var typeLib = TypeLibWrapper.FromVBProject(project))
960+
{
961+
return GetDocumentClassControlType(typeLib, documentClassName, controlName);
962+
}
963+
}
964+
965+
/// <summary>
966+
/// Returns the class progID of a control on a UserForm
967+
/// </summary>
968+
/// <param name="projectTypeLib">Low-level ITypeLib wrapper representing the VBA project</param>
969+
/// <param name="documentClassName">Document class name, as declared in the VBA project</param>
970+
/// <param name="controlName">Control name, as declared on the UserForm</param>
971+
/// <returns>string class progID of the specified control on a UserForm, e.g. "MSForms.CommandButton"</returns>
972+
public static string GetDocumentClassControlType(TypeLibWrapper projectTypeLib, string documentClassName, string controlName)
973+
{
974+
return GetDocumentClassControlType(projectTypeLib.TypeInfos.Get(documentClassName), controlName);
975+
}
976+
977+
/// <summary>
978+
/// Returns the class progID of a control on a UserForm
979+
/// </summary>
980+
/// <param name="ide">Safe-com wrapper representing the UserForm VBA component</param>
981+
/// <param name="controlName">Control name, as declared on the UserForm</param>
982+
/// <returns>string class progID of the specified control on a UserForm, e.g. "MSForms.CommandButton"</returns>
983+
public static string GetDocumentClassControlType(IVBComponent component, string controlName)
984+
{
985+
return GetDocumentClassControlType(component.ParentProject, component.Name, controlName);
986+
}
817987

988+
/// <summary>
989+
/// Returns the class progID of a control on a UserForm
990+
/// </summary>
991+
/// <param name="documentClass">Low-level ITypeLib wrapper representing the UserForm VBA component</param>
992+
/// <param name="controlName">Control name, as declared on the UserForm</param>
993+
/// <returns>string class progID of the specified control on a UserForm, e.g. "MSForms.CommandButton"</returns>
994+
public static string GetDocumentClassControlType(TypeInfoWrapper documentClass, string controlName)
995+
{
996+
return documentClass.GetSafeImplementedTypeInfo(0).GetControlType(controlName).GetProgID();
997+
}
998+
818999
/// <summary>
8191000
/// Retreives the TYPEFLAGS of a VBA component (e.g. module/class), providing flags like TYPEFLAG_FCANCREATE, TYPEFLAG_FPREDECLID
8201001
/// </summary>
@@ -944,6 +1125,7 @@ public static string DocumentAll(IVBE ide)
9441125
{
9451126
typeLib.Document(output);
9461127
}
1128+
9471129
return output.ToString();
9481130
}
9491131
}

Rubberduck.VBEEditor/ComManagement/TypeLibs/TypeLibsAbstract.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public interface IVBEComponent
7676
void GetSomeRelatedTypeInfoPtrs(out IntPtr A, out IntPtr B); // returns 2 TypeInfos, seemingly related to this ITypeInfo, but slightly different.
7777
}
7878

79-
// An extended version of ITypeInfo, hosted by the VBE that includes a particularly helpful member, GetStdModInstance
79+
// An extended version of ITypeInfo, hosted by the VBE that includes a particularly helpful member, GetStdModAccessor
8080
[ComImport(), Guid("CACC1E82-622B-11D2-AA78-00C04F9901D2")]
8181
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
8282
public interface IVBETypeInfo
@@ -102,7 +102,7 @@ public interface IVBETypeInfo
102102
void ReleaseVarDesc(IntPtr pVarDesc);
103103

104104
void Placeholder1();
105-
IDispatch GetStdModInstance(); // a handy extra vtable entry we can use to invoke members in standard modules.
105+
IDispatch GetStdModAccessor(); // a handy extra vtable entry we can use to invoke members in standard modules.
106106
}
107107

108108
// A compatible version of ITypeLib, where COM objects are outputted as IntPtrs instead of objects

0 commit comments

Comments
 (0)