我有一个COM函数应该通过LPSAFEARRAY*out参数返回一个SafeArray .该函数使用ATL的CComSafeArray模板类创建SafeArray .我的天真实现用于CComSafeArray<T>::Detach()将所有权从局部变量移动到输出参数:
void foo(LPSAFEARRAY* psa)
{
CComSafeArray<VARIANT> ret;
ret.Add(CComVariant(42));
*psa = ret.Detach();
}
int main()
{
CComSafeArray<VARIANT> sa;
foo(sa.GetSafeArrayPtr());
std::cout << sa[0].lVal << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
问题是CComSafeArray::Detach()执行一个Unlock操作,以便当SafeArray的新所有者(sa在这种情况下为main )被销毁时,锁定不为零并且Destroy无法解锁SafeArray E_UNEXPECTED(这导致内存泄漏,因为SafeArray不是解除分配).
通过COM方法边界将所有权转移到CComSafeArrays的正确方法是什么?
编辑:从单个答案到目前为止似乎错误是在客户端(main)而不是从服务器端(foo),但我发现很难相信这CComSafeArray不是为这个琐碎的用例设计的,必须是一种优雅的方法,可以将COMA方法中的SafeArray转换为CComSafeArray.
我想创建一个小应用程序来添加和删除注册表中的用户定义的上下文菜单项.为了实现这一点,我需要以某种方式获取任意DLL的CLSID,以便在写入新条目之前,如果它们存在,我可以备份它们.
虽然regsrv32不知怎的设法创造了这个神奇的数字,但我没有找到任何办法自己获得这个数字.
我希望有比这更好的东西:
如果DLL已重命名,我可以看到出现问题的可能性.
当您从.NET代码接口COM对象时,VS会创建一个带有互操作类的互操作DLL.
例:
你有一个foo.dll实现了一个COM库Foo,它包含了一个COM接口"IBar"的实现.您将foo.dll的引用添加到.NET项目.在/ bin中,您将看到一个Interop.FooLib.dll.在对象浏览器中,您将看到Interop.FooLib,在其下您将看到FooLib,您将看到BarClass,您将在该Bar和IBar下看到Base Types.
在.NET代码中,当声明变量时,可以键入FooLib,而intellisense将为您提供Bar或BarClass()的选项.
根据我的理解,你在变量声明中使用它并不重要,但是它对于它的构造函数非常重要.
也就是说,这两个都应该有效:
FooLib.BarClass theBar = new FooLib.BarClass();
FooLib.Bar theBar = new FooLib.BarClass();
Run Code Online (Sandbox Code Playgroud)
但这不应该奏效:
FooLib.Bar theBar = new FooLib.Bar();
Run Code Online (Sandbox Code Playgroud)
这是问题所在.我们刚刚找到了一个奇怪的错误,其中代码适用于某些客户,并且在我们的开发和测试环境中工作,但是在一个客户站点上工作,结果是使用Bar()构造函数的程序员.
那么,任何人都可以准确地解释两个构造函数Bar()和BarClass()之间的区别吗?
任何人都可以解释为什么Bar()构造函数似乎有效吗?
任何人都可以提供一种方法来确保没有人错误地调用错误的构造函数,而不读取每行代码吗?
- 添加 -
有人提出问题出在我们的COM实施中.这就是我们正在做的事情:
IDL:
[
object,
uuid(...),
dual,
helpstring("IBar Interface"),
pointer_default(unique),
nonextensible
]
interface IBar : IDispatch
{
[id(1), helpstring("method barify")]
HRESULT barify([out, retval] VARIANT_BOOL *rVal);
// ...
};
// ...
[
uuid(...),
version(1.0),
helpstring("Foo 1.0 Type Library")
]
library FooLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
// ...
[
uuid(...),
helpstring("Bar Class")
] …Run Code Online (Sandbox Code Playgroud) 我正试图弄清楚如何在页面内的Javascript中调用我的BHO对象中的C#方法.我在C++/ATL/Com中找到了很多关于如何做的页面,例如:
我试图在C#中正确地实现它,但是我无法让它工作,可能是因为我有一些明显的COM问题,我并不完全理解.
我正在使用C#4.0.
以下是代码的相关部分:
using SHDocVw;
using mshtml;
using System.Runtime.InteropServices;
[ComVisible(true),
Guid("300736C4-DCDA-4DB0-90AD-4510A12EBBC6"),
ClassInterface(ClassInterfaceType.None),
ProgId("My Extension")]
public class BrowserHelperObject : IObjectWithSite
{
const int DISPATCH_PROPERTYPUT = 4;
const int FDEX_NAME_ENSURE = 2;
const uint LOCALE_USER_DEFAULT = 0x0400;
WebBrowser browser;
...
public void OnDocumentComplete(dynamic frame, ref dynamic url)
{
...
var window = browser.Document.parentWindow;
int pid = 0;
window.GetDispId("myExtension", FDEX_NAME_ENSURE, ref pid);
System.Runtime.InteropServices.ComTypes.DISPPARAMS dispParms = new System.Runtime.InteropServices.ComTypes.DISPPARAMS();
dispParms.cArgs = 1;
dispParms.cNamedArgs = 0;
dispParms.rgvarg = ???;
dispParms.rgdispidNamedArgs = IntPtr.Zero;
System.Runtime.InteropServices.ComTypes.EXCEPINFO …Run Code Online (Sandbox Code Playgroud) 我已经使用pywin32在python中编写了一个脚本,将pdf文件保存到文本中,直到最近才能正常工作.我在Excel中使用类似的方法.代码如下:
def __pdf2Txt(self, pdf, fileformat="com.adobe.acrobat.accesstext"):
outputLoc = os.path.dirname(pdf)
outputLoc = os.path.join(outputLoc, os.path.splitext(os.path.basename(pdf))[0] + '.txt')
try:
win32com.client.gencache.EnsureModule('{E64169B3-3592-47d2-816E-602C5C13F328}', 0, 1, 1)
adobe = win32com.client.DispatchEx('AcroExch.App')
pdDoc = win32com.client.DispatchEx('AcroExch.PDDoc')
pdDoc.Open(pdf)
jObject = pdDoc.GetJSObject()
jObject.SaveAs(outputLoc, "com.adobe.acrobat.accesstext")
except:
traceback.print_exc()
return False
finally:
del jObject
pdDoc.Close()
del pdDoc
adobe.Exit()
del adobe
Run Code Online (Sandbox Code Playgroud)
但是这段代码突然停止工作,我得到以下输出:
Traceback (most recent call last):
File "C:\Documents and Settings\ablishen\workspace\HooverKeyCreator\src\HooverKeyCreator.py", line 38, in __pdf2Txt
jObject.SaveAs(outputLoc, "com.adobe.acrobat.accesstext")
File "C:\Python27\lib\site-packages\win32com\client\dynamic.py", line 505, in __getattr__
ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
com_error: (-2147467263, 'Not implemented', None, None)
False
Run Code Online (Sandbox Code Playgroud)
我有类似的用VB编写的代码可以正常工作,所以我猜它与COM接口没有正确绑定到适当的函数有关吗?(我的COM知识不完整).
我正在尝试编写一些使用UPnP进行NAT遍历的代码(仅供家庭使用),使用C#4和Microsoft的基于COM的NAT遍历API(Hnetcfg.dll).
不幸的是(或者幸运的是)我最后一次在.NET中使用COM互操作是在最后一个冰河时代的某个时候,我似乎对C#使用动态类型进行互操作以及如何编写回调感到困惑(所以COM服务器调用我的托管代码).
这是令人兴奋的几行代码:
// Referencing COM NATUPNPLib ("NATUPnP 1.0 Type Library")
using System;
using NATUPNPLib;
class NATUPnPExample
{
public delegate void NewNumberOfEntriesDelegate(int lNewNumberOfEntries);
public static void NewNumberOfEntries(int lNewNumberOfEntries)
{
Console.WriteLine("New number of entries: {0}", lNewNumberOfEntries);
}
public static void Main(string[] args)
{
UPnPNAT nat = new UPnPNAT();
NewNumberOfEntriesDelegate numberOfEntriesCallback = NewNumberOfEntries;
nat.NATEventManager.NumberOfEntriesCallback = numberOfEntriesCallback;
nat.StaticPortMappingCollection.Add(4555, "TCP", 4555, "192.168.0.1", true, "UPnPNAT Test");
// Presumably my NewNumberOfEntries() method should be called by the COM component about now
nat.StaticPortMappingCollection.Remove(4555, "TCP");
} …Run Code Online (Sandbox Code Playgroud) 为什么dynamic对象在反射时不能在NameTranslate COM对象上调用这些方法?
使用动态的失败示例:
Type ntt = Type.GetTypeFromProgID("NameTranslate");
dynamic nto = Activator.CreateInstance(ntt);
nto.Init(3,null)
Run Code Online (Sandbox Code Playgroud)
第三行因NotImplementedException而失败,并且消息未实现方法或操作.
使用不同COM对象(WScript.Shell和SendKeys)的类似尝试:
Type shellType = Type.GetTypeFromProgID("WScript.Shell");
dynamic shell = Activator.CreateInstance(shellType);
shell.SendKeys("abc");
Run Code Online (Sandbox Code Playgroud)
回到第一个样本.如果我使用反射并使用InvokeMethod方法调用方法,一切正常.
使用反射的工作示例:
Type ntt = Type.GetTypeFromProgID("NameTranslate");
object nto = Activator.CreateInstance(ntt);
object[] initParams = new object[]{3,null};
ntt.InvokeMember("Init", BindingFlags.InvokeMethod, null, nto, initParams);
Run Code Online (Sandbox Code Playgroud)
我相信这必须与如何创建或标记COM对象有关 - 但是对于我的生活,我在文档,对象浏览器或注册表中看不到任何指示这些COM对象及其子/函数被标记的内容私有或其他通常会抛弃dynamic关键字的东西.
MSDN上的NameTranslate文档:http://msdn.microsoft.com/en-us/library/windows/desktop/aa706046.aspx
问题: 如何在通用Windows平台(UWP)应用程序中创建COM对象?
动机: 我想从WPF切换到UWP.由于我的工作量要求只能通过COM访问第三方库(据我所知),我需要从UWP进行COM调用.
语境:
背景
在Visual Studio 2013(Visual Studio 2015中的"经典桌面"项目)中,我使用了C#代码
// Conceptual:
DotNetInterface comObjectInstance =
(DotNetInterface)Microsoft.VisualBasic.Interaction.CreateObject(
"this string specified the COM object type"
);
// Example: Open Excel via COM:
Excel.Application oApp = (Excel.Application)Interaction.CreateObject("Excel.Application");
Run Code Online (Sandbox Code Playgroud)
Visual Studio项目需要Microsoft.VisualBasic使用Interaction.CreateObject()和COM对象的类型库的引用.
我想在Windows 10 Education上的Visual Studio 2015 Enterprise生成的通用Windows平台(UWP)应用程序中使用此C#代码.我能够添加对COM对象的类型库的引用,但无法引用,Microsoft.VisualBasic因为它没有出现在Visual Studio的Reference Manager中.
思考,尝试过的解决方案,猜测等
我添加了对"UWP的Windows桌面扩展"的引用,希望它可以启用对正常.NET功能的调用,但还没有弄清楚如何使用它.
我认为即使UWP应用程序从根本上不能进行COM调用,我们至少可以构造一个调用正常.NET程序的包装器(即使通过网络端口),这反过来又可以运行COM调用.由于即使在最糟糕的情况下显然也可以解决这个问题,我觉得微软提供的COM对象应该是(并且可能是)提供的解决方案.但我想自从UWP这么新以来,在线文档很稀疏,现在很难找到.
更新#1
发现了MSDN文章,Win32和COM for Windows Runtime应用程序和通用Windows平台(UWP)应用程序,声称WinRT应用程序(包括UWP应用程序)只能使用COM对象的子集.MSDN建议使用受支持的COM API元素或从不受支持的COM API迁移到功能替换.
我找到一种方法对我的第三方库进行COM调用后,通过谷歌搜索运行时错误找到了这篇文章.错误:
mscorlib.ni.dll中出现"System.Runtime.InteropServices.COMException"类型的异常,但未在用户代码中处理
附加信息:由于以下错误,使用CoCreateInstanceFromApp创建具有CLSID {[edit:GUID已删除]}的COM组件实例失败:80040154未注册类(HRESULT异常:0x80040154(REGDB_E_CLASSNOTREG)).请确保您的COM对象位于允许的CoCreateInstanceFromApp列表中.
我仍然不确定是否有一种内置方式来访问我的第三方库的COM API.如果没有,这可能意味着我将不得不使用网络端口或其他东西制作我自己的包装器,这似乎是错误的.
假设您在名为的模块中有此代码Module1:
Option Explicit
Private Type TSomething
Foo As Integer
Bar As Integer
End Type
Public Something As TSomething
Run Code Online (Sandbox Code Playgroud)
在等效的C#代码中,如果您创建了Something字段public,代码将不再编译,因为可访问性不一致 - 字段的类型比字段本身更不易访问.这是有道理的.
但是在VBA中,您可以使用以下代码Module2:
Sub DoSomething()
Module1.Something.Bar = 42
Debug.Print Module1.Something.Bar
End Sub
Run Code Online (Sandbox Code Playgroud)
并且在键入时获得IntelliSense,它会编译,运行并输出42.
为什么?从COM的角度来看,它是如何工作的?它是语言规范的一部分吗?
你能帮我理解在Win32平台上我的Delphi应用程序中FPU Control Word的运行情况.
当我们创建一个新的VCL应用程序时,控制字设置为1372h.这是我不明白的第一件事,为什么它是1372h而不是1332h,这是Default8087CW在System单位中定义的.
这两者之间的区别:
1001101110010 //1372h
1001100110010 //1332h
Run Code Online (Sandbox Code Playgroud)
是根据文档保留或不使用的第6位.
第二个问题是CreateOleObject.
function CreateOleObject(const ClassName: string): IDispatch;
var
ClassID: TCLSID;
begin
try
ClassID := ProgIDToClassID(ClassName);
{$IFDEF CPUX86}
try
Set8087CW( Default8087CW or $08);
{$ENDIF CPUX86}
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result));
{$IFDEF CPUX86}
finally
Reset8087CW;
end;
{$ENDIF CPUX86}
except
on E: EOleSysError do
raise EOleSysError.Create(Format('%s, ProgID: "%s"',[E.Message, ClassName]),E.ErrorCode,0) { Do not localize }
end;
end;
Run Code Online (Sandbox Code Playgroud)
上面的功能是将控制字改为137Ah,所以它打开第3位(溢出掩码).我不明白为什么要调用它Reset8087CW,而不是恢复进入函数之前的单词状态?