use crate::{
cilassembly::{ChangeRefRc, CilAssembly},
metadata::{
marshalling::{encode_marshalling_descriptor, MarshallingInfo, NativeType, NATIVE_TYPE},
tables::{CodedIndex, CodedIndexType, FieldMarshalRaw, TableDataOwned, TableId},
token::Token,
},
Error, Result,
};
pub struct FieldMarshalBuilder {
parent: Option<CodedIndex>,
native_type: Option<Vec<u8>>,
encoding_error: Option<Error>,
}
impl Default for FieldMarshalBuilder {
fn default() -> Self {
Self::new()
}
}
impl FieldMarshalBuilder {
#[must_use]
pub fn new() -> Self {
Self {
parent: None,
native_type: None,
encoding_error: None,
}
}
#[must_use]
pub fn parent(mut self, parent: CodedIndex) -> Self {
self.parent = Some(parent);
self
}
#[must_use]
pub fn native_type(mut self, native_type: &[u8]) -> Self {
self.native_type = Some(native_type.to_vec());
self
}
#[must_use]
pub fn simple_native_type(mut self, type_id: u8) -> Self {
self.native_type = Some(vec![type_id]);
self
}
#[must_use]
pub fn unicode_string(self) -> Self {
self.simple_native_type(NATIVE_TYPE::LPWSTR)
}
#[must_use]
pub fn ansi_string(self) -> Self {
self.simple_native_type(NATIVE_TYPE::LPSTR)
}
#[must_use]
pub fn fixed_array(mut self, element_type: u8, size: u32) -> Self {
let mut descriptor = vec![NATIVE_TYPE::ARRAY, element_type];
descriptor.extend_from_slice(&size.to_le_bytes());
self.native_type = Some(descriptor);
self
}
#[must_use]
pub fn com_interface(self) -> Self {
self.simple_native_type(NATIVE_TYPE::INTERFACE)
}
#[must_use]
pub fn native_type_spec(mut self, native_type: NativeType) -> Self {
let info = MarshallingInfo {
primary_type: native_type,
additional_types: vec![],
};
match encode_marshalling_descriptor(&info) {
Ok(descriptor) => self.native_type = Some(descriptor),
Err(e) => self.encoding_error = Some(e),
}
self
}
#[must_use]
pub fn marshalling_info(mut self, info: &MarshallingInfo) -> Self {
match encode_marshalling_descriptor(info) {
Ok(descriptor) => self.native_type = Some(descriptor),
Err(e) => self.encoding_error = Some(e),
}
self
}
#[must_use]
pub fn pointer(self, ref_type: Option<NativeType>) -> Self {
let ptr_type = NativeType::Ptr {
ref_type: ref_type.map(Box::new),
};
self.native_type_spec(ptr_type)
}
#[must_use]
pub fn variable_array(
self,
element_type: NativeType,
size_param: Option<u32>,
element_count: Option<u32>,
) -> Self {
let array_type = NativeType::Array {
element_type: Box::new(element_type),
num_param: size_param,
num_element: element_count,
};
self.native_type_spec(array_type)
}
#[must_use]
pub fn fixed_array_typed(self, element_type: Option<NativeType>, size: u32) -> Self {
let array_type = NativeType::FixedArray {
element_type: element_type.map(Box::new),
size,
};
self.native_type_spec(array_type)
}
#[must_use]
pub fn native_struct(self, packing_size: Option<u8>, class_size: Option<u32>) -> Self {
let struct_type = NativeType::Struct {
packing_size,
class_size,
};
self.native_type_spec(struct_type)
}
#[must_use]
pub fn safe_array(self, variant_type: u16, user_defined_name: Option<String>) -> Self {
let array_type = NativeType::SafeArray {
variant_type,
user_defined_name,
};
self.native_type_spec(array_type)
}
#[must_use]
pub fn custom_marshaler(
self,
guid: &str,
native_type_name: &str,
cookie: &str,
type_reference: &str,
) -> Self {
let marshaler_type = NativeType::CustomMarshaler {
guid: guid.to_string(),
native_type_name: native_type_name.to_string(),
cookie: cookie.to_string(),
type_reference: type_reference.to_string(),
};
self.native_type_spec(marshaler_type)
}
pub fn build(self, assembly: &mut CilAssembly) -> Result<ChangeRefRc> {
if let Some(encoding_error) = self.encoding_error {
return Err(encoding_error);
}
let parent = self
.parent
.ok_or_else(|| Error::ModificationInvalid("Marshal parent is required".to_string()))?;
let native_type = self.native_type.ok_or_else(|| {
Error::ModificationInvalid("Native type descriptor is required".to_string())
})?;
if native_type.is_empty() {
return Err(Error::ModificationInvalid(
"Native type descriptor cannot be empty".to_string(),
));
}
let valid_parent_tables = CodedIndexType::HasFieldMarshal.tables();
if !valid_parent_tables.contains(&parent.tag) {
return Err(Error::ModificationInvalid(format!(
"Parent must be a HasFieldMarshal coded index (Field/Param), got {:?}",
parent.tag
)));
}
let native_type_index = assembly.blob_add(&native_type)?.placeholder();
let rid = assembly.next_rid(TableId::FieldMarshal)?;
let token = Token::from_parts(TableId::FieldMarshal, rid);
let field_marshal_raw = FieldMarshalRaw {
rid,
token,
offset: 0, parent,
native_type: native_type_index,
};
assembly.table_row_add(
TableId::FieldMarshal,
TableDataOwned::FieldMarshal(field_marshal_raw),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
cilassembly::{ChangeRefKind, CilAssembly},
metadata::cilassemblyview::CilAssemblyView,
};
use std::path::PathBuf;
#[test]
fn test_field_marshal_builder_basic() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal); let marshal_descriptor = vec![NATIVE_TYPE::I4];
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.native_type(&marshal_descriptor)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_different_parents() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let marshal_descriptor = vec![NATIVE_TYPE::I4];
let field_parent = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let field_marshal_ref = FieldMarshalBuilder::new()
.parent(field_parent)
.native_type(&marshal_descriptor)
.build(&mut assembly)
.unwrap();
let param_parent = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let param_marshal_ref = FieldMarshalBuilder::new()
.parent(param_parent)
.native_type(&marshal_descriptor)
.build(&mut assembly)
.unwrap();
assert_eq!(
field_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
param_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert!(!std::sync::Arc::ptr_eq(
&field_marshal_ref,
¶m_marshal_ref
));
}
}
#[test]
fn test_field_marshal_builder_different_native_types() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_refs: Vec<_> = (1..=8)
.map(|i| CodedIndex::new(TableId::Param, i, CodedIndexType::HasFieldMarshal))
.collect();
let int_marshal_ref = FieldMarshalBuilder::new()
.parent(param_refs[0].clone())
.simple_native_type(NATIVE_TYPE::I4)
.build(&mut assembly)
.unwrap();
let unicode_marshal_ref = FieldMarshalBuilder::new()
.parent(param_refs[1].clone())
.unicode_string()
.build(&mut assembly)
.unwrap();
let ansi_marshal_ref = FieldMarshalBuilder::new()
.parent(param_refs[2].clone())
.ansi_string()
.build(&mut assembly)
.unwrap();
let array_marshal_ref = FieldMarshalBuilder::new()
.parent(param_refs[3].clone())
.fixed_array(NATIVE_TYPE::I1, 32) .build(&mut assembly)
.unwrap();
let interface_marshal_ref = FieldMarshalBuilder::new()
.parent(param_refs[4].clone())
.com_interface()
.build(&mut assembly)
.unwrap();
assert_eq!(
int_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
unicode_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
ansi_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
array_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
interface_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
let refs = [
&int_marshal_ref,
&unicode_marshal_ref,
&ansi_marshal_ref,
&array_marshal_ref,
&interface_marshal_ref,
];
for i in 0..refs.len() {
for j in i + 1..refs.len() {
assert!(!std::sync::Arc::ptr_eq(refs[i], refs[j]));
}
}
}
}
#[test]
fn test_field_marshal_builder_complex_descriptors() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let field_ref = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let complex_array_descriptor = vec![
NATIVE_TYPE::ARRAY,
NATIVE_TYPE::I4, 0x02, 0x10,
0x00,
0x00,
0x00, 0x00,
0x00,
0x00,
0x00, ];
let marshal_ref = FieldMarshalBuilder::new()
.parent(field_ref)
.native_type(&complex_array_descriptor)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_missing_parent() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let marshal_descriptor = vec![NATIVE_TYPE::I4];
let result = FieldMarshalBuilder::new()
.native_type(&marshal_descriptor)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_field_marshal_builder_missing_native_type() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let result = FieldMarshalBuilder::new()
.parent(param_ref)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_field_marshal_builder_empty_native_type() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let empty_descriptor = vec![];
let result = FieldMarshalBuilder::new()
.parent(param_ref)
.native_type(&empty_descriptor)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_field_marshal_builder_invalid_parent_type() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let invalid_parent =
CodedIndex::new(TableId::TypeDef, 1, CodedIndexType::HasFieldMarshal); let marshal_descriptor = vec![NATIVE_TYPE::I4];
let result = FieldMarshalBuilder::new()
.parent(invalid_parent)
.native_type(&marshal_descriptor)
.build(&mut assembly);
assert!(result.is_err());
}
}
#[test]
fn test_field_marshal_builder_all_primitive_types() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let primitive_types = [
NATIVE_TYPE::BOOLEAN,
NATIVE_TYPE::I1,
NATIVE_TYPE::U1,
NATIVE_TYPE::I2,
NATIVE_TYPE::U2,
NATIVE_TYPE::I4,
NATIVE_TYPE::U4,
NATIVE_TYPE::I8,
NATIVE_TYPE::U8,
NATIVE_TYPE::R4,
NATIVE_TYPE::R8,
NATIVE_TYPE::INT,
NATIVE_TYPE::UINT,
];
for (i, &native_type) in primitive_types.iter().enumerate() {
let param_ref = CodedIndex::new(
TableId::Param,
(i + 1) as u32,
CodedIndexType::HasFieldMarshal,
);
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.simple_native_type(native_type)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
}
#[test]
fn test_field_marshal_builder_string_types() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param1 = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let param2 = CodedIndex::new(TableId::Param, 2, CodedIndexType::HasFieldMarshal);
let param3 = CodedIndex::new(TableId::Param, 3, CodedIndexType::HasFieldMarshal);
let param4 = CodedIndex::new(TableId::Param, 4, CodedIndexType::HasFieldMarshal);
let ansi_marshal_ref = FieldMarshalBuilder::new()
.parent(param1)
.simple_native_type(NATIVE_TYPE::LPSTR)
.build(&mut assembly)
.unwrap();
let unicode_marshal_ref = FieldMarshalBuilder::new()
.parent(param2)
.simple_native_type(NATIVE_TYPE::LPWSTR)
.build(&mut assembly)
.unwrap();
let bstr_marshal_ref = FieldMarshalBuilder::new()
.parent(param3)
.simple_native_type(NATIVE_TYPE::BSTR)
.build(&mut assembly)
.unwrap();
let byval_marshal_ref = FieldMarshalBuilder::new()
.parent(param4)
.simple_native_type(NATIVE_TYPE::BYVALSTR)
.build(&mut assembly)
.unwrap();
assert_eq!(
ansi_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
unicode_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
bstr_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
byval_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_realistic_pinvoke() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let path_param = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let path_marshal_ref = FieldMarshalBuilder::new()
.parent(path_param)
.unicode_string() .build(&mut assembly)
.unwrap();
let security_param =
CodedIndex::new(TableId::Param, 2, CodedIndexType::HasFieldMarshal);
let security_marshal_ref = FieldMarshalBuilder::new()
.parent(security_param)
.simple_native_type(NATIVE_TYPE::PTR) .build(&mut assembly)
.unwrap();
let return_param = CodedIndex::new(TableId::Param, 0, CodedIndexType::HasFieldMarshal); let return_marshal_ref = FieldMarshalBuilder::new()
.parent(return_param)
.simple_native_type(NATIVE_TYPE::I4) .build(&mut assembly)
.unwrap();
assert_eq!(
path_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
security_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
return_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert!(!std::sync::Arc::ptr_eq(
&path_marshal_ref,
&security_marshal_ref
));
assert!(!std::sync::Arc::ptr_eq(
&path_marshal_ref,
&return_marshal_ref
));
assert!(!std::sync::Arc::ptr_eq(
&security_marshal_ref,
&return_marshal_ref
));
}
}
#[test]
fn test_field_marshal_builder_struct_fields() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let x_field = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let y_field = CodedIndex::new(TableId::Field, 2, CodedIndexType::HasFieldMarshal);
let x_marshal_ref = FieldMarshalBuilder::new()
.parent(x_field)
.simple_native_type(NATIVE_TYPE::I4)
.build(&mut assembly)
.unwrap();
let y_marshal_ref = FieldMarshalBuilder::new()
.parent(y_field)
.simple_native_type(NATIVE_TYPE::I4)
.build(&mut assembly)
.unwrap();
assert_eq!(
x_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert_eq!(
y_marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
assert!(!std::sync::Arc::ptr_eq(&x_marshal_ref, &y_marshal_ref));
}
}
#[test]
fn test_field_marshal_builder_high_level_native_type_spec() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.native_type_spec(NativeType::LPWStr {
size_param_index: Some(2),
})
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_variable_array() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let field_ref = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(field_ref)
.variable_array(NativeType::I4, Some(1), Some(10))
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_fixed_array_typed() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let field_ref = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(field_ref)
.fixed_array_typed(Some(NativeType::Boolean), 64)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_native_struct() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let field_ref = CodedIndex::new(TableId::Field, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(field_ref)
.native_struct(Some(4), Some(128))
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_pointer() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.pointer(Some(NativeType::I4))
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_custom_marshaler() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.custom_marshaler(
"12345678-1234-5678-9ABC-DEF012345678",
"NativeType",
"cookie_data",
"MyAssembly.CustomMarshaler",
)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_safe_array() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.safe_array(crate::metadata::marshalling::VARIANT_TYPE::I4, None)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
#[test]
fn test_field_marshal_builder_marshalling_info() {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/samples/WindowsBase.dll");
if let Ok(view) = CilAssemblyView::from_path(&path) {
let mut assembly = CilAssembly::new(view);
let param_ref = CodedIndex::new(TableId::Param, 1, CodedIndexType::HasFieldMarshal);
let info = MarshallingInfo {
primary_type: NativeType::LPStr {
size_param_index: Some(1),
},
additional_types: vec![NativeType::Boolean],
};
let marshal_ref = FieldMarshalBuilder::new()
.parent(param_ref)
.marshalling_info(&info)
.build(&mut assembly)
.unwrap();
assert_eq!(
marshal_ref.kind(),
ChangeRefKind::TableRow(TableId::FieldMarshal)
);
}
}
}