2009-02-22 13 views
6
'a geri aktarın.

Harici bir uygulama ile kullanılması gereken yöntemleri içeren bir C# sınıfı kitaplığım var. Ne yazık ki bu harici uygulama sadece C/C++ 'da harici API'leri desteklemektedir.Bir diziyi C++ ile C# arasında taşımanın en kolay yolu, onu değiştirin ve C++

Şimdi bir C++ dll ve C# DLL arasında çalışan çok basit bir COM örneği elde etmeyi başardım, ancak dizi verilerinin etrafında nasıl hareket edebileceğime takılıyorum.

Bu benim COM yoluyla iletişim web'de bulunan sadece biraz örnek olarak, şimdiye kadar ne var ise: Bu benim C# sınıfında eklenti yöntemini çağırmak için çalışıyor

DLL_EXPORT(void) runAddTest(int add1,long *result) { 
    // Initialize COM. 
    HRESULT hr = CoInitialize(NULL); 

    // Create the interface pointer. 
    IUnitModelPtr pIUnit(__uuidof(UnitModel)); 

    long lResult = 0; 

    // Call the Add method. 
    pIUnit->Add(5, 10, &lResult); 

    *result = lResult; 

    // Uninitialize COM. 
    CoUninitialize(); 

} 

. Bir çiftler dizisini almak ve döndürmek için bunu nasıl değiştirebilirim? (hasta aynı zamanda çizgiden aşağı dizelerle de yapmalı).

Yönetilmeyen bir dizi kullanmam gerekiyor, bu diziyi bazı hesaplamalar için C# sınıfına geçirmeliyim ve ardından sonuçları özgün işlev çağrısı (yönetilmeyen) C++ içinde belirtilen dizi başvurusuna iletin.

böyle bir işlevi açığa gerekir:


* kalsin - katına diziye referans

* calcOut - değeri - çiftlerde

Numin dizisine referans girdi dizisinin boyutu

DLL_EXPORT(void) doCalc(double *calcIn, int numIn, double *calcOut) 
{ 
     //pass the calcIn array to C# class for the calcuations 

     //get the values back from my C# class 

     //put the values from the C# class 
     //into the array ref specified by the *calcOut reference 


} 

Ben düşünüyorum Harici uygulama için bir C++ \ CLI DLL kullanabilirim, bu yüzden bu düz COM'dan daha kolaysa, o zaman buna bakmak isteyeceğim.

Öncelikli olarak bir C# geliştiricisiyim, ancak Interop ve C++'un derin sonlarında atılmış olduğum için lütfen nazik olun.

cevap

3

Bunu bir süre önce denedim ama ne yazık ki nasıl bir araya getirildiğini unutmuşum ... amacım için yavaş yavaş olduğu ortaya çıktı ve böylece C# 'yi kestim ve tüm C++' ya geri döndüm. Öncelikle bir C# geliştiricisi olduğunuzu söylediğinizde, umarım işaretçiler anlayabilirsiniz çünkü eğer nazik olmanın bir yolu yoksa.

Geçme diziler temelde aşağı C++ tarafında (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) üzerindeki fonksiyonların CoTaskMemAlloc ailesini ve C# tarafında (- AllocCoTaskMem gibi yöntemleri vardır http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx) üzerine Mareşal sınıfını kullanarak geldi.

public class serviceUtils 
{ 
    unsafe public long stringToCoTaskPtr(ref str thestring) 
    { 
     return (long)Marshal.StringToCoTaskMemAnsi(thestring.theString).ToPointer();//TODO : what errors occur from here? handle them 
    } 

    unsafe public long bytesToCoTaskPtr(ref bytes thebytes, ref short byteCnt) 
    { 
     byteCnt = (short)thebytes.theArray.Length; 
     IntPtr tmpptr = new IntPtr(); 
     tmpptr = Marshal.AllocCoTaskMem(byteCnt); 
     Marshal.Copy(thebytes.theArray, 0, tmpptr, byteCnt); 
     return (long)tmpptr.ToPointer(); 
    } 

    public void freeCoTaskMemPtr(long ptr) 
    { 
     Marshal.FreeCoTaskMem(new IntPtr(ptr));//TODO : errors from here? 
    } 

    public string coTaskPtrToString(long theptr) 
    { 
     return Marshal.PtrToStringAnsi(new IntPtr(theptr)); 
    } 

    public byte[] coTaskPtrToBytes(long theptr, short thelen) 
    { 
     byte[] tmpbytes = new byte[thelen]; 
     Marshal.Copy(new IntPtr(theptr), tmpbytes, 0, thelen); 
     return tmpbytes; 
    } 
} 

Sadece sana biraz daha kod atmak: C# için bir yardımcı program sınıfı ile sona erdi bu C++

#import "..\COMClient\bin\Debug\COMClient.tlb" named_guids raw_interfaces_only 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
CoInitialize(NULL); //Initialize all COM Components 
COMClient::IComCalculatorPtr pCalc; 
// CreateInstance parameters 
HRESULT hRes = pCalc.CreateInstance(COMClient::CLSID_ComCalculator); 
if (hRes == S_OK) { 
    long size = 5; 
    LPVOID ptr = CoTaskMemAlloc(size); 
    if(ptr != NULL) 
    { 
     memcpy(ptr, "12345", size); 
     short ans = 0; 
     pCalc->changeBytes((__int64*)&ptr, &size, &ans); 
     CoTaskMemFree(ptr); 
    } 
} 

CoUninitialize(); //DeInitialize all COM Components 

return 0; 
} 

public short changeBytes(ref long ptr, ref int arraysize) 
    { 
     try 
     { 
      IntPtr interopPtr = new IntPtr(ptr);     
      testservice.ByteArray bytes = new testservice.ByteArray(); 
      byte[] somebytes = new byte[arraysize]; 
      Marshal.Copy(interopPtr, somebytes, 0, arraysize); 
      bytes.theArray = somebytes; 

      CalculatorClient client = generateClient(); 
      client.takeArray(ref bytes); 
      client.Close(); 
      if (arraysize < bytes.theArray.Length) 
      { 
       interopPtr = Marshal.ReAllocCoTaskMem(interopPtr, bytes.theArray.Length);//TODO : throws an exception if fails... deal with it 
      } 
      Marshal.Copy(bytes.theArray, 0, interopPtr, bytes.theArray.Length); 
      ptr = interopPtr.ToInt64(); 

      arraysize = bytes.theArray.Length; 

      //TODO : do we need to free IntPtr? check all code for memory leaks... check for successful allocation 
     } 
     catch(Exception e) 
     { 
      return 3; 
     } 

     return 2; 
    } 

Maalesef bu C# denir, ama ben Tüm bunları çözmek için zamanınız yok ve düzgün bir şekilde açıklayın, umarım bu, size en azından bazı şeyleri doğru yönde yönlendirir. İyi Şanslar

Not: Bu bilgiyi ağdan yazmak için gereken tüm bilgileri aldım, bu yüzden orada.

1

Harici uygulama için bir C++ \ CLI DLL kullanabileceğimi düşünüyorum, bu yüzden bu düz COM'dan daha kolaysa, o zaman buna bakmak isteyeceğim.

olasılıkla daha kolay olacaktır daha sonra 3 rd parti boyunca C++/CLI sarıcı çok daha COM deneyimi yok (ve diziler COM anlamlı basit değildir) ise. yerine fazladan bir adım daha - (> yönetilen < yerli) yönetilen < için ihtiyaç duyacakları gerekli COM Çağrı Wrapper -

Ayrıca sadece tek bir manevra sahne içerecektir> COM arabirim).

0

Bu da işe yarar mı?

C#

, 1. Çağrı Marshal.PtrToStructure 2. değeri değiştirmek 3. Arama Marshal.StructureToPtr