COM编程的好处就是可以跨语言,我们公司是用C#做开发的,所以用C++去开发COM组件就可以弥补图形渲染性能上的不足。但COM组件也有它的缺点,就是COM接口本身的调用开销比较大,所以我总结了,使用COM组件最好是通过最少次数的接口调用来达到目的。所以数组的传递就至关重要了。打个最简单的比方,调10000次SetX( double x )花的时间肯定要比一次性把x数组传进去SetX( double[] x )多的多。
普通的值类型数组比如int[],short[]什么的,这个都好说,COM里都有对应的类型(VTS_PI4 VTS_PI2),而且就算是在C#里也可以用Marshal来分配非托管数组然后传递到COM里去进行操作。但字符串数组不同,它每一项长度都不一样,这给传递带来了一些困难。所以今天主要就说说字符串数组的传递。
在COM里数组传递要用到一个叫做SAFEARRAY的东东包装一下。但是如果直接用SAFEARRAY进行传递,那么到C#里面的参数类型是IntPtr,还是一个指针,这样就给数据的获取带来不便。后来发现数组传递之前还要用VARIANT这个数据结构把SAFEARRAY再次包装一下,这样数据传到C#里后参数类型是Object,只需要用String[]强转一下就OK了,十分方便。其实我这段描述百度百科里好像也说的挺明白的,可以参考一下:http://baike.baidu.com/view/1907445.htm
我们可以这样理解,SAFEARRAY就是存放数据的数组,而VARIANT就是一个壳子,就好比C#里装箱的概念一样,把变量变成Object后传递起来会方便一些。说的够多了,下面直接用代码举例了。
首先,如果数组是传入参数的话,调度声明中需要用VTS_VARIANT,函数原型声明中类型是VARIANT*;如果是返回值的话调度声明要用VT_VARIANT,函数原型声明类型是VARIANT(注意不是VARIANT*,和传入参数有点不同,否则到C#里参数类型不是Object而是IntPtr);然后主要说下数组的构造和包装。
VARIANT AryWarp;
VariantInit( &AryWarp );
AryWarp.vt = VT_ARRAY | VT_BSTR; // 指定壳子是用来包装数组的,并且是字符串数组,注意COM里面字符串就是BSTR,其实就是宽字符串
SAFEARRAY* psa;
SAFEARRAYBOUND bounds = { 10, 0 };
psa = SafeArrayCreate( VT_BSTR, 1, &bounds ); // 这里就是初始化一个长度为10的字符串数组
然后声明一个字符串数组 BSTR* bstrArray; 并且用SafeArrayAccessData将psa与bstrArray进行绑定(不知道绑定这个词合适不合适,反正给我的感觉就是绑定),然后要做的就是对bstrArray[ i ]赋值了,赋值完毕后记得用SafeArrayUnaccessData解除绑定。
最后一步就是包壳子了, AryWarp.parray = psa; 然后就可以把AryWarp return了。
自己写的调用例子保存
// DLGeXEDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "DLGeXE.h"
#include "DLGeXEDlg.h"
#include "afxdialogex.h"
#include <math.h>
#include <string>
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//#import ".\Debug/AtlDll1.dll" no_namespace //导入Dicom文件解析组件
#import ".\Debug/ComOpenDicomFile.dll" no_namespace //导入Dicom文件解析组件
//#import ".\Debug/ComFileProcessing.dll" no_namespace //导入Dicom文件解析组件
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
UINT ThreadFunction(LPVOID pParam );
static DWORD WINAPI ThreadB1(LPVOID lpParam);
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CDLGeXEDlg 对话框
CDLGeXEDlg::CDLGeXEDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CDLGeXEDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CDLGeXEDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDLGeXEDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CDLGeXEDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_BUTTON1, &CDLGeXEDlg::OnBnClickedButton1)
ON_WM_TIMER()
END_MESSAGE_MAP()
// CDLGeXEDlg 消息处理程序
BOOL CDLGeXEDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CDLGeXEDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CDLGeXEDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDLGeXEDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CDLGeXEDlg::OnBnClickedOk()
{
//ExitProcess(0);
// TODO: 在此添加控件通知处理程序代码
//********************************************************************************************************
//AtlDll1.dll(第一版函数有点乱的版本com组件)
//********************************************************************************************************
//IIDicomDS * dsPtr;//Dicom解析组件
//CoInitialize(NULL);
//CoCreateInstance(__uuidof(IDicomDS) ,NULL,CLSCTX_ALL,__uuidof(IIDicomDS),(void**)&dsPtr);//组件实例化 //类型转换
//
打开dcm文件
//HRESULT ret;
//ret = dsPtr->LoadDicomFile(_T("C:\\Untitled-52.dcm"));
ret = dsPtr->LoadDicomFile(_T("C:\\1.3.46.670589.11.32566.5.20.1.1.3360.2013070508422193616.dcm"));
获取DCM文件名
BSTR p;
dsPtr->getFileName(&p);
//char chName[256] = {0};
//dsPtr->GetFileName(chName);
获取DCM的数据行数
//long nImageLine = 0;
//nImageLine = dsPtr->GetImageLine(nImageLine);
获取DCM的数据列数
//long nImageCloumn = 0;
//nImageCloumn = dsPtr->GetImageCloumn(nImageCloumn);
获取DCM的数据Buffer
//short *m_pBuff;
//m_pBuff=new SHORT[nImageLine*nImageCloumn];
//dsPtr->getBuff(m_pBuff);
//FILE *fp1 = fopen("C:\\HHTmp\\DicomTmp\\IfcviewpHAPPY","wb");
//fwrite(m_pBuff, nImageLine*nImageCloumn, sizeof(SHORT), fp1);
//fclose(fp1);
释放dicom
//dsPtr->GetDicomBuffer(m_pBuff);
//dsPtr->Release();
//CoUninitialize();
//return;
//CDialogEx::OnOK();
//********************************************************************************************************
//ComOpenDicomFile.dll
//********************************************************************************************************
IIDicomDp *dicomDp;
CoInitialize(NULL);
CoCreateInstance(__uuidof(IDicomDp) ,NULL,CLSCTX_ALL,__uuidof(IIDicomDp),(void**)&dicomDp);//组件实例化 //类型转换
CString fileName = _T("D:\\printdir\\1.2.826.0.1.3680043.2.42.18398468180992.2388.201505181849180.dcm");
dicomDp->LoadDicomFile(_bstr_t(fileName));
int nInstanceNumber = 0;
dicomDp->GetInstanceNumber(&nInstanceNumber);
//获取DCM的数据行数
int nImageLine = 0;
int nImageCloumn = 0;
dicomDp->raw_GetColumnAndRow(&nImageCloumn,&nImageLine);
CString strBmpName = _T("d:\\printdir\\2222.bmp");
//dicomDp->SaveDcmToZoomBmp(256,256,_bstr_t(strBmpName),1);
//CString fileName1 = _T("C:\\Untitled-521.dcm");
//dicomDp->LoadDicomFile(_bstr_t(fileName1));
//int nInstanceNumber1 = 0;
//dicomDp->GetInstanceNumber(&nInstanceNumber1);
//dicomDp->ReleaseDicomFile();
int nReportSize = 0;
dicomDp->GetReprotSize(&nReportSize);
//获取DCM的数据Buffer
VARIANT * m_pBuff;
m_pBuff=new VARIANT[nReportSize];
dicomDp->GetReportBuff(m_pBuff);
FILE *fp1 = fopen("D:\\printdir\\rportTest11.pdf","wb");
fwrite(m_pBuff, nReportSize, sizeof(VARIANT), fp1);
fclose(fp1);
//dicomDp->GetDsaFrameBuffer(20,m_pBuff);
char * m_pBuff1;
m_pBuff1=new char[nReportSize];
SAFEARRAY *psa = m_pBuff->parray; //使用数组整理读取的数据
for (long i = 0; i < nReportSize; i++)
{
::SafeArrayGetElement(psa,&i,m_pBuff1+i);
}
FILE *fp2= fopen("D:\\printdir\\rportTest22.pdf","wb");
fwrite(m_pBuff1, nReportSize, sizeof(char), fp2);
fclose(fp2);
//dicomDp->TestVar();
//dicomDp->SaveWebMeasure(_T("C:\\Untitled-jdlkfjaslkdf.dcm"),_T("C:\\Untitled-jdlkfjaslkdf1111.dcm"),_T("lcblcb"));
//CString strMeasureText = _T("");
//BSTR bStr = SysAllocString(_T(""));
//dicomDp->GetMeasureText(&bStr);
dicomDp->ReleaseDicomFile();
//********************************************************************************************************
//ComFileProcessing.dll
//********************************************************************************************************
//解析dicom文件,这里用lead
//CString strFilePath = L"C:\\1.dcm";
//IIDicomDS * dsPtr; //Dicom解析组件
//CoInitialize(NULL);
//CoCreateInstance(__uuidof(IDicomDS) ,NULL,CLSCTX_ALL,__uuidof(IIDicomDS),(void**)&dsPtr); //组件实例化
//CComBSTR bstrPath = strFilePath.AllocSysString(); //类型转换
//HRESULT ret;
ret = dsPtr->LoadFile(bstr_t(bstrPath), 0, 0);
//ret = dsPtr->LoadDicomFile(bstr_t(bstrPath), 0, 5, 0);
//CString strInstanceNumber = _T("");
dsPtr->InsertElementStr(_bstr_t(strInstanceNumber),0x00200013);//插入病人姓名
//BSTR bStr = SysAllocString(_T(""));
dsPtr->GetElementStr(&bStr,0x00100010);
//dsPtr->GetElementStr(&bStr,0x00204000);
//
//dsPtr->GetElementStr(&bStr,0x00080018);
//dsPtr->SaveFile("C:\\111111111.bmp",1);
//dsPtr->CloseFile();
//CoUninitialize();//释放COM
AfxMessageBox(L"OK");
}
void CDLGeXEDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
//CLog m_log(_T("C:\\dddddd.log"));
//m_log.logWrite(_T("1"));
//m_log.logWrite(_T("2"));
//m_log.logWrite(_T("3"));
//m_log.logWrite(_T("4"));
//m_log.logClose();
CString str = L"病人姓名:";
string s(CW2A((LPCTSTR)str));
CString str2(CA2W(s.c_str()));
int iType = 1;
m_QueryThread = AfxBeginThread(ThreadFunction,this,THREAD_PRIORITY_NORMAL,
0, CREATE_SUSPENDED, NULL);
//SetTimer(1,1000,NULL);
m_pHEvent = m_QueryThread->m_hThread;//用于监测线程是否退出
//SetEvent(m_hEvent);//用于通知线程退出,现在是启动
//
//PostThreadMessage(GetCurrentThreadId(), 0x0401,100,86);
//MSG msg;
//PeekMessage(&msg, NULL, 0x0401, 0x0401, PM_NOREMOVE);
//
m_QueryThread->ResumeThread();
if(WAIT_OBJECT_0 == WaitForSingleObject(m_pHEvent, 0))
{
MessageBox(_T("in"),_T("提示"),MB_OK);
return;
}
double result;
double x = 4.0;
result = exp(x);
}
void CDLGeXEDlg::test()
{
if(WAIT_TIMEOUT == WaitForSingleObject(m_hEvent, 0))
{
MessageBox(_T("in Thread"),_T("提示"),MB_OK);
return;
}
}
UINT ThreadFunction(LPVOID pParam )
{
Sleep(2000);
CDLGeXEDlg * pDlg = (CDLGeXEDlg*)pParam;
int a = 10;
int b = 100;
//pDlg->test();
return 0;
}
BOOL CDLGeXEDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (pMsg->message == 0x0401)
{
AfxMessageBox(_T("收到消息 "));
}
return CDialogEx::PreTranslateMessage(pMsg);
}
void CDLGeXEDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch(nIDEvent)
{
case 1:
MessageBox(_T("in onTimer"),_T("提示"),MB_OK);
break;
default:
break;
}
CDialogEx::OnTimer(nIDEvent);
}