package winmd import ( "fmt" "github.com/go-kit/log" "github.com/tdakkota/win32metadata/md" "github.com/tdakkota/win32metadata/types" ) // ClassNotFoundError is returned when a class is not found. type ClassNotFoundError struct { Class string } func (e *ClassNotFoundError) Error() string { return fmt.Sprintf("class %s was not found", e.Class) } // Store holds the windows metadata contexts. It can be used to get the metadata across multiple files. type Store struct { contexts map[string]*types.Context logger log.Logger } // NewStore loads all windows metadata files and returns a new Store. func NewStore(logger log.Logger) (*Store, error) { contexts := make(map[string]*types.Context) winmdFiles, err := allFiles() if err != nil { return nil, err } // parse and store all files in memory for _, f := range winmdFiles { winmdCtx, err := parseWinMDFile(f.Name()) if err != nil { return nil, err } contexts[f.Name()] = winmdCtx } return &Store{ contexts: contexts, logger: logger, }, nil } func parseWinMDFile(path string) (*types.Context, error) { f, err := open(path) if err != nil { return nil, err } defer func() { _ = f.Close() }() return types.FromPE(f) } // TypeDefByName returns a type definition that matches the given name. func (mds *Store) TypeDefByName(class string) (*TypeDef, error) { // the type can belong to any of the contexts for _, ctx := range mds.contexts { if td := mds.typeDefByNameAndCtx(class, ctx); td != nil { return td, nil // return the first match } } return nil, &ClassNotFoundError{Class: class} } func (mds *Store) typeDefByNameAndCtx(class string, ctx *types.Context) *TypeDef { typeDefTable := ctx.Table(md.TypeDef) for i := uint32(0); i < typeDefTable.RowCount(); i++ { var typeDef types.TypeDef if err := typeDef.FromRow(typeDefTable.Row(i)); err != nil { continue // keep searching instead of failing } if typeDef.TypeNamespace+"."+typeDef.TypeName == class { return &TypeDef{ TypeDef: typeDef, HasContext: HasContext{ctx}, logger: mds.logger, } } } return nil }