mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 06:13:40 +01:00
Cleanup
This commit is contained in:
parent
bb76f0eea4
commit
6c8c42ca05
3 changed files with 225 additions and 135 deletions
|
|
@ -41,31 +41,8 @@ internal sealed partial class TextureManager
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var wrapDispose = leaveWrapOpen ? null : wrap;
|
using var wrapDispose = leaveWrapOpen ? null : wrap;
|
||||||
var texDesc = default(D3D11_TEXTURE2D_DESC);
|
|
||||||
|
|
||||||
unsafe
|
var dxgiFormat = this.GetFormatOf(wrap);
|
||||||
{
|
|
||||||
using var texSrv = default(ComPtr<ID3D11ShaderResourceView>);
|
|
||||||
using var context = default(ComPtr<ID3D11DeviceContext>);
|
|
||||||
using var tex2D = default(ComPtr<ID3D11Texture2D>);
|
|
||||||
fixed (Guid* piid = &IID.IID_ID3D11ShaderResourceView)
|
|
||||||
((IUnknown*)wrap.ImGuiHandle)->QueryInterface(piid, (void**)texSrv.GetAddressOf()).ThrowOnError();
|
|
||||||
|
|
||||||
this.Device.Get()->GetImmediateContext(context.GetAddressOf());
|
|
||||||
|
|
||||||
using (var texRes = default(ComPtr<ID3D11Resource>))
|
|
||||||
{
|
|
||||||
texSrv.Get()->GetResource(texRes.GetAddressOf());
|
|
||||||
|
|
||||||
using var tex2DTemp = default(ComPtr<ID3D11Texture2D>);
|
|
||||||
texRes.As(&tex2DTemp).ThrowOnError();
|
|
||||||
tex2D.Swap(&tex2DTemp);
|
|
||||||
}
|
|
||||||
|
|
||||||
tex2D.Get()->GetDesc(&texDesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dxgiFormat = texDesc.Format;
|
|
||||||
if (!WicManager.GetCorrespondingWicPixelFormat(dxgiFormat, out _, out _))
|
if (!WicManager.GetCorrespondingWicPixelFormat(dxgiFormat, out _, out _))
|
||||||
dxgiFormat = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
dxgiFormat = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
|
|
@ -73,10 +50,7 @@ internal sealed partial class TextureManager
|
||||||
|
|
||||||
var (specs, bytes) = await this.GetRawImageAsync(
|
var (specs, bytes) = await this.GetRawImageAsync(
|
||||||
wrap,
|
wrap,
|
||||||
new()
|
new() { Format = dxgiFormat },
|
||||||
{
|
|
||||||
Format = dxgiFormat,
|
|
||||||
},
|
|
||||||
true,
|
true,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
@ -102,14 +76,29 @@ internal sealed partial class TextureManager
|
||||||
var pathTemp = $"{path}.{GetCurrentThreadId():X08}{Environment.TickCount64:X16}.tmp";
|
var pathTemp = $"{path}.{GetCurrentThreadId():X08}{Environment.TickCount64:X16}.tmp";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await this.SaveToStreamAsync(
|
var dxgiFormat = this.GetFormatOf(wrap);
|
||||||
wrap,
|
if (!WicManager.GetCorrespondingWicPixelFormat(dxgiFormat, out _, out _))
|
||||||
|
dxgiFormat = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
|
using var istream = TerraFxComInterfaceExtensions.CreateIStreamFromFile(
|
||||||
|
pathTemp,
|
||||||
|
FileMode.Create,
|
||||||
|
FileAccess.Write,
|
||||||
|
FileShare.None);
|
||||||
|
|
||||||
|
var (specs, bytes) = await this.GetRawImageAsync(
|
||||||
|
wrap,
|
||||||
|
new() { Format = dxgiFormat },
|
||||||
|
true,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
this.Wic.SaveToStreamUsingWic(
|
||||||
|
specs,
|
||||||
|
bytes,
|
||||||
containerGuid,
|
containerGuid,
|
||||||
File.Create(pathTemp),
|
istream,
|
||||||
props,
|
props,
|
||||||
leaveWrapOpen: true,
|
cancellationToken);
|
||||||
leaveStreamOpen: false,
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -182,7 +171,7 @@ internal sealed partial class TextureManager
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var handle = bytes.Pin();
|
using var handle = bytes.Pin();
|
||||||
using var stream = this.Wic.CreateIStreamFromMemory(handle, bytes.Length);
|
using var stream = this.Wic.CreateIStreamViewOfMemory(handle, bytes.Length);
|
||||||
return this.Wic.NoThrottleCreateFromWicStream(stream, cancellationToken);
|
return this.Wic.NoThrottleCreateFromWicStream(stream, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception e1)
|
catch (Exception e1)
|
||||||
|
|
@ -212,7 +201,11 @@ internal sealed partial class TextureManager
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var stream = this.Wic.CreateIStreamFromFile(path);
|
using var stream = TerraFxComInterfaceExtensions.CreateIStreamFromFile(
|
||||||
|
path,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.Read);
|
||||||
return this.Wic.NoThrottleCreateFromWicStream(stream, cancellationToken);
|
return this.Wic.NoThrottleCreateFromWicStream(stream, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception e1)
|
catch (Exception e1)
|
||||||
|
|
@ -228,6 +221,26 @@ internal sealed partial class TextureManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe DXGI_FORMAT GetFormatOf(IDalamudTextureWrap wrap)
|
||||||
|
{
|
||||||
|
using var texSrv = default(ComPtr<ID3D11ShaderResourceView>);
|
||||||
|
using var context = default(ComPtr<ID3D11DeviceContext>);
|
||||||
|
fixed (Guid* piid = &IID.IID_ID3D11ShaderResourceView)
|
||||||
|
((IUnknown*)wrap.ImGuiHandle)->QueryInterface(piid, (void**)texSrv.GetAddressOf()).ThrowOnError();
|
||||||
|
|
||||||
|
this.Device.Get()->GetImmediateContext(context.GetAddressOf());
|
||||||
|
|
||||||
|
using var texRes = default(ComPtr<ID3D11Resource>);
|
||||||
|
texSrv.Get()->GetResource(texRes.GetAddressOf());
|
||||||
|
|
||||||
|
using var tex2D = default(ComPtr<ID3D11Texture2D>);
|
||||||
|
texRes.As(&tex2D).ThrowOnError();
|
||||||
|
|
||||||
|
var texDesc = default(D3D11_TEXTURE2D_DESC);
|
||||||
|
tex2D.Get()->GetDesc(&texDesc);
|
||||||
|
return texDesc.Format;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>A part of texture manager that uses Windows Imaging Component under the hood.</summary>
|
/// <summary>A part of texture manager that uses Windows Imaging Component under the hood.</summary>
|
||||||
internal sealed class WicManager : IDisposable
|
internal sealed class WicManager : IDisposable
|
||||||
{
|
{
|
||||||
|
|
@ -355,7 +368,7 @@ internal sealed partial class TextureManager
|
||||||
/// <param name="handle">An instance of <see cref="MemoryHandle"/>.</param>
|
/// <param name="handle">An instance of <see cref="MemoryHandle"/>.</param>
|
||||||
/// <param name="length">The number of bytes in the memory.</param>
|
/// <param name="length">The number of bytes in the memory.</param>
|
||||||
/// <returns>The new instance of <see cref="IStream"/>.</returns>
|
/// <returns>The new instance of <see cref="IStream"/>.</returns>
|
||||||
public unsafe ComPtr<IStream> CreateIStreamFromMemory(MemoryHandle handle, int length)
|
public unsafe ComPtr<IStream> CreateIStreamViewOfMemory(MemoryHandle handle, int length)
|
||||||
{
|
{
|
||||||
using var wicStream = default(ComPtr<IWICStream>);
|
using var wicStream = default(ComPtr<IWICStream>);
|
||||||
this.wicFactory.Get()->CreateStream(wicStream.GetAddressOf()).ThrowOnError();
|
this.wicFactory.Get()->CreateStream(wicStream.GetAddressOf()).ThrowOnError();
|
||||||
|
|
@ -366,21 +379,6 @@ internal sealed partial class TextureManager
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Creates a new instance of <see cref="IStream"/> from a file path.</summary>
|
|
||||||
/// <param name="path">The file path.</param>
|
|
||||||
/// <returns>The new instance of <see cref="IStream"/>.</returns>
|
|
||||||
public unsafe ComPtr<IStream> CreateIStreamFromFile(string path)
|
|
||||||
{
|
|
||||||
using var wicStream = default(ComPtr<IWICStream>);
|
|
||||||
this.wicFactory.Get()->CreateStream(wicStream.GetAddressOf()).ThrowOnError();
|
|
||||||
fixed (char* pPath = path)
|
|
||||||
wicStream.Get()->InitializeFromFilename((ushort*)pPath, GENERIC_READ).ThrowOnError();
|
|
||||||
|
|
||||||
var res = default(ComPtr<IStream>);
|
|
||||||
wicStream.As(ref res).ThrowOnError();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Creates a new instance of <see cref="IDalamudTextureWrap"/> from a <see cref="IStream"/>.</summary>
|
/// <summary>Creates a new instance of <see cref="IDalamudTextureWrap"/> from a <see cref="IStream"/>.</summary>
|
||||||
/// <param name="stream">The stream that will NOT be closed after.</param>
|
/// <param name="stream">The stream that will NOT be closed after.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using TerraFX.Interop.Windows;
|
|
||||||
|
|
||||||
using static TerraFX.Interop.Windows.Windows;
|
|
||||||
|
|
||||||
namespace Dalamud.Utility.TerraFxCom;
|
|
||||||
|
|
||||||
/// <summary>Utilities for <see cref="IUnknown"/> and its derivatives.</summary>
|
|
||||||
internal static unsafe partial class TerraFxComInterfaceExtensions
|
|
||||||
{
|
|
||||||
/// <summary>Calls <see cref="IPropertyBag2.Write"/>.</summary>
|
|
||||||
/// <param name="obj">The property bag.</param>
|
|
||||||
/// <param name="name">The name of the item to be interpreted as a VARIANT.</param>
|
|
||||||
/// <param name="value">The new value, to be interpreted as a <see cref="VARIANT"/>.</param>
|
|
||||||
/// <returns>Return value from <inheritdoc cref="IPropertyBag2.Write"/>.</returns>
|
|
||||||
public static HRESULT Write(ref this IPropertyBag2 obj, string name, object? value)
|
|
||||||
{
|
|
||||||
VARIANT varValue;
|
|
||||||
// Marshal calls VariantInit.
|
|
||||||
Marshal.GetNativeVariantForObject(value, (nint)(&varValue));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fixed (char* pName = name)
|
|
||||||
{
|
|
||||||
var option = new PROPBAG2 { pstrName = (ushort*)pName };
|
|
||||||
return obj.Write(1, &option, &varValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
VariantClear(&varValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Calls <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</summary>
|
|
||||||
/// <param name="obj">The object.</param>
|
|
||||||
/// <param name="name">The name of the metadata.</param>
|
|
||||||
/// <param name="value">The new value, to be interpreted as a <see cref="PROPVARIANT"/>.</param>
|
|
||||||
/// <returns>Return value from <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</returns>
|
|
||||||
public static HRESULT SetMetadataByName(ref this IWICMetadataQueryWriter obj, string name, object? value)
|
|
||||||
{
|
|
||||||
VARIANT varValue;
|
|
||||||
// Marshal calls VariantInit.
|
|
||||||
Marshal.GetNativeVariantForObject(value, (nint)(&varValue));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PROPVARIANT propVarValue;
|
|
||||||
var propVarRes = VariantToPropVariant(&varValue, &propVarValue);
|
|
||||||
if (propVarRes < 0)
|
|
||||||
return propVarRes;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fixed (char* pName = name)
|
|
||||||
return obj.SetMetadataByName((ushort*)pName, &propVarValue);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_ = PropVariantClear(&propVarValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_ = VariantClear(&varValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Calls <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</summary>
|
|
||||||
/// <param name="obj">The object.</param>
|
|
||||||
/// <param name="name">The name of the metadata.</param>
|
|
||||||
/// <returns>Return value from <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</returns>
|
|
||||||
public static HRESULT RemoveMetadataByName(ref this IWICMetadataQueryWriter obj, string name)
|
|
||||||
{
|
|
||||||
fixed (char* pName = name)
|
|
||||||
return obj.RemoveMetadataByName((ushort*)pName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[LibraryImport("propsys.dll")]
|
|
||||||
private static partial int VariantToPropVariant(
|
|
||||||
void* pVarIn,
|
|
||||||
void* pPropVarOut);
|
|
||||||
}
|
|
||||||
175
Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs
Normal file
175
Dalamud/Utility/TerraFxCom/TerraFxComInterfaceExtensions.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
|
namespace Dalamud.Utility.TerraFxCom;
|
||||||
|
|
||||||
|
/// <summary>Utilities for <see cref="IUnknown"/> and its derivatives.</summary>
|
||||||
|
internal static unsafe partial class TerraFxComInterfaceExtensions
|
||||||
|
{
|
||||||
|
/// <summary>Creates a new instance of <see cref="IStream"/> from a file path.</summary>
|
||||||
|
/// <param name="path">The file path.</param>
|
||||||
|
/// <param name="mode">The file open mode.</param>
|
||||||
|
/// <param name="access">The file access mode.</param>
|
||||||
|
/// <param name="share">The file share mode.</param>
|
||||||
|
/// <param name="attributes">The file attributes.</param>
|
||||||
|
/// <returns>The new instance of <see cref="IStream"/>.</returns>
|
||||||
|
public static ComPtr<IStream> CreateIStreamFromFile(
|
||||||
|
string path,
|
||||||
|
FileMode mode,
|
||||||
|
FileAccess access,
|
||||||
|
FileShare share,
|
||||||
|
FileAttributes attributes = FileAttributes.Normal)
|
||||||
|
{
|
||||||
|
var grfMode = 0u;
|
||||||
|
bool fCreate;
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case FileMode.CreateNew:
|
||||||
|
fCreate = true;
|
||||||
|
grfMode |= STGM.STGM_FAILIFTHERE;
|
||||||
|
break;
|
||||||
|
case FileMode.Create:
|
||||||
|
fCreate = true;
|
||||||
|
grfMode |= STGM.STGM_CREATE;
|
||||||
|
break;
|
||||||
|
case FileMode.Open:
|
||||||
|
fCreate = false;
|
||||||
|
grfMode |= STGM.STGM_FAILIFTHERE; // yes
|
||||||
|
break;
|
||||||
|
case FileMode.OpenOrCreate:
|
||||||
|
throw new NotSupportedException(
|
||||||
|
$"${FileMode.OpenOrCreate} is not supported. It might be, but it needs testing.");
|
||||||
|
case FileMode.Append:
|
||||||
|
throw new NotSupportedException($"${FileMode.Append} is not supported.");
|
||||||
|
case FileMode.Truncate:
|
||||||
|
throw new NotSupportedException($"${FileMode.Truncate} is not supported.");
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (access)
|
||||||
|
{
|
||||||
|
case FileAccess.Read:
|
||||||
|
grfMode |= STGM.STGM_READ;
|
||||||
|
break;
|
||||||
|
case FileAccess.Write:
|
||||||
|
grfMode |= STGM.STGM_WRITE;
|
||||||
|
break;
|
||||||
|
case FileAccess.ReadWrite:
|
||||||
|
grfMode |= STGM.STGM_READWRITE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(access), access, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (share)
|
||||||
|
{
|
||||||
|
case FileShare.None:
|
||||||
|
grfMode |= STGM.STGM_SHARE_EXCLUSIVE;
|
||||||
|
break;
|
||||||
|
case FileShare.Read:
|
||||||
|
grfMode |= STGM.STGM_SHARE_DENY_WRITE;
|
||||||
|
break;
|
||||||
|
case FileShare.Write:
|
||||||
|
grfMode |= STGM.STGM_SHARE_DENY_READ;
|
||||||
|
break;
|
||||||
|
case FileShare.ReadWrite:
|
||||||
|
grfMode |= STGM.STGM_SHARE_DENY_NONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Only ${FileShare.Read} and ${FileShare.Write} are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = default(ComPtr<IStream>);
|
||||||
|
fixed (char* pPath = path)
|
||||||
|
{
|
||||||
|
SHCreateStreamOnFileEx(
|
||||||
|
(ushort*)pPath,
|
||||||
|
grfMode,
|
||||||
|
(uint)attributes,
|
||||||
|
fCreate,
|
||||||
|
null,
|
||||||
|
stream.GetAddressOf()).ThrowOnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = default(ComPtr<IStream>);
|
||||||
|
stream.As(ref res).ThrowOnError();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Calls <see cref="IPropertyBag2.Write"/>.</summary>
|
||||||
|
/// <param name="obj">The property bag.</param>
|
||||||
|
/// <param name="name">The name of the item to be interpreted as a VARIANT.</param>
|
||||||
|
/// <param name="value">The new value, to be interpreted as a <see cref="VARIANT"/>.</param>
|
||||||
|
/// <returns>Return value from <inheritdoc cref="IPropertyBag2.Write"/>.</returns>
|
||||||
|
public static HRESULT Write(ref this IPropertyBag2 obj, string name, object? value)
|
||||||
|
{
|
||||||
|
VARIANT varValue;
|
||||||
|
// Marshal calls VariantInit.
|
||||||
|
Marshal.GetNativeVariantForObject(value, (nint)(&varValue));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fixed (char* pName = name)
|
||||||
|
{
|
||||||
|
var option = new PROPBAG2 { pstrName = (ushort*)pName };
|
||||||
|
return obj.Write(1, &option, &varValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
VariantClear(&varValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Calls <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</summary>
|
||||||
|
/// <param name="obj">The object.</param>
|
||||||
|
/// <param name="name">The name of the metadata.</param>
|
||||||
|
/// <param name="value">The new value, to be interpreted as a <see cref="PROPVARIANT"/>.</param>
|
||||||
|
/// <returns>Return value from <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</returns>
|
||||||
|
public static HRESULT SetMetadataByName(ref this IWICMetadataQueryWriter obj, string name, object? value)
|
||||||
|
{
|
||||||
|
VARIANT varValue;
|
||||||
|
// Marshal calls VariantInit.
|
||||||
|
Marshal.GetNativeVariantForObject(value, (nint)(&varValue));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PROPVARIANT propVarValue;
|
||||||
|
var propVarRes = VariantToPropVariant(&varValue, &propVarValue);
|
||||||
|
if (propVarRes < 0)
|
||||||
|
return propVarRes;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fixed (char* pName = name)
|
||||||
|
return obj.SetMetadataByName((ushort*)pName, &propVarValue);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ = PropVariantClear(&propVarValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ = VariantClear(&varValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Calls <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</summary>
|
||||||
|
/// <param name="obj">The object.</param>
|
||||||
|
/// <param name="name">The name of the metadata.</param>
|
||||||
|
/// <returns>Return value from <inheritdoc cref="IWICMetadataQueryWriter.SetMetadataByName"/>.</returns>
|
||||||
|
public static HRESULT RemoveMetadataByName(ref this IWICMetadataQueryWriter obj, string name)
|
||||||
|
{
|
||||||
|
fixed (char* pName = name)
|
||||||
|
return obj.RemoveMetadataByName((ushort*)pName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport("propsys.dll")]
|
||||||
|
private static partial int VariantToPropVariant(
|
||||||
|
void* pVarIn,
|
||||||
|
void* pPropVarOut);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue