问题
i got a native 32 bit dll (no source) which runs as a plugin in an application i use. I've done another native dll myself which will communicate with that plugin in order to create and update the plugin's controls. From that dll i've exported the functions I need in order to control the plugin from my c# application (with p/invoke).
here's the code:
h file:
#pragma once
#include "include\SpoutControls.h"
extern "C" { __declspec(dllexport) void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text); }
extern "C" { __declspec(dllexport) bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls); }
extern "C" { __declspec(dllexport) void CloseControls(); }
//
extern "C" __declspec(dllexport) int ReleaseMemory(float *pArray)
{
delete[] pArray;
//delete[] Usize;
return 0;
};
the cpp:
#include "SpoutControls4vvvv.h"
//SpoutControls and the functions
//CreateControl, OpenControls, CheckControls, CloseControls
//are declared in SpoutControls.h, which comes with the 32 bit plugin dll
SpoutControls spoutcontrols;
void InitializeControls(char *sendername, int *numControls, char** names, int *types, float* floats, float* toggles, float* press, char** text) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v=0, t=0, p=0, s = 0;
for (int controlID = 0; controlID < all; controlID++) {
if (types[controlID] == 0) {
spoutcontrols.CreateControl(names[controlID], "float",0.0,1.0, floats[v]);
v++;
}
if (types[controlID] == 1) {
spoutcontrols.CreateControl(names[controlID], "bool", toggles[t]);
t++;
}
if (types[controlID] == 2) {
spoutcontrols.CreateControl(names[controlID], "event", press[p]);
p++;
}
if (types[controlID] == 3) {
spoutcontrols.CreateControl(names[controlID], "text", text[s]);
s++;
}
}
spoutcontrols.OpenControls(sendername);
}
bool UpdateControls(const char** text, float *floats, float *toggles, float *press, int *numControls) {
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
int all = Vcontrols + Tcontrols + Pcontrols + Scontrols;
int v = 0, t = 0, p = 0, s = 0;
if (spoutcontrols.CheckControls(myControls)) {
for (int controlID = 0; controlID < all; controlID++) {
if (myControls[controlID].type == 10) {
floats[v] = myControls[controlID].value;
v++;
}
if (myControls[controlID].type == 0) {
toggles[t] = myControls[controlID].value;
t++;
}
if (myControls[controlID].type == 1) {
press[p] = myControls[controlID].value;
p++;
}
if (myControls[controlID].type == 100) {
text[s] = myControls[controlID].text.data();
s++;
}
}
return true;
}
return false;
}
void CloseControls() {
spoutcontrols.CloseControls();
}
and here's the c# code:
public unsafe class SystemSpoutSenderNode: IDisposable
{
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern void InitializeControls(IntPtr sendername, IntPtr numControls,String[] names, IntPtr types, IntPtr floats, IntPtr toggles, IntPtr press, String[] text);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int CloseControls();
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool UpdateControls([In, Out] String[] text, [In, Out] float[] floats, [In, Out] float[] toggles, [In, Out] float[] press, IntPtr numControls);
[System.Runtime.InteropServices.DllImport("SpoutControls4vvvv.dll")]
private static extern int ReleaseMemory(IntPtr ptr);
public void Evaluate(int SpreadMax)
{
//countControls determines number of controls per type (string,float,toggle,click)
int[] controls = countControls(FType);
//sumControls will just add up all elements in controls
int all = sumControls(controls);
//in my code these arrays will get filled with values, deleted here for readability
String[] names = new String[all];
int[] types = new int[all];
float[] floats = new float[controls[0]];
float[] toggles = new float[controls[1]];
float[] press = new float[controls[2]];
String[] text = new String[controls[3]];
//initialze return arrays
String[] Rtext = new String[controls[3]];
float[] Rfloats = new float[controls[0]];
float[] Rtoggles = new float[controls[1]];
float[] Rpress = new float[controls[2]];
//allocate pointers
IntPtr SndrNamePtr = NativeUtf8FromString(FSenderName);
IntPtr BinPtr = Marshal.AllocHGlobal(4*sizeof(int));
IntPtr TypePtr = Marshal.AllocHGlobal(all*sizeof(int));
IntPtr FloatPtr = Marshal.AllocHGlobal(controls[0]*sizeof(float));
IntPtr TogglePtr = Marshal.AllocHGlobal(controls[1]*sizeof(float));
IntPtr PressPtr = Marshal.AllocHGlobal(controls[2]*sizeof(float));
try
{
//copy control info + defaults to pointer
Marshal.Copy(controls, 0, BinPtr, 4);
Marshal.Copy(types, 0, TypePtr, all);
Marshal.Copy(floats, 0, FloatPtr, controls[0]);
Marshal.Copy(toggles, 0, TogglePtr, controls[1]);
Marshal.Copy(press, 0, PressPtr, controls[2]);
//initialize controls
if (FWrite) InitializeControls(SndrNamePtr,BinPtr,names,TypePtr,FloatPtr,TogglePtr,PressPtr,text);
//update controls
bool changed = UpdateControls(Rtext,Rfloats,Rtoggles,Rpress,BinPtr);
//FF, FT, FS and FP are the outputs in my c# host
if (changed){
for(int j=0; j<controls[0];j++){
FF[j]=Rfloats[j];
}
for(int j=0; j<controls[1];j++){
FT[j]=FloatToBool(Rtoggles[j]);
}
for(int j=0; j<controls[3];j++){
FS[j]=Rtext[j];
}
}
for(int j=0; j<controls[2];j++){
FP[j]=FloatToBool(Rpress[j]);
}
}
finally
{
Marshal.FreeHGlobal(SndrNamePtr);
Marshal.FreeHGlobal(BinPtr);
Marshal.FreeHGlobal(FloatPtr);
Marshal.FreeHGlobal(TogglePtr);
Marshal.FreeHGlobal(PressPtr);
}
}
}
}
public void Dispose()
{
CleanUp();
CloseControls();
}
}
NOTE: the c# code runs without precompiling in a frame-based, c# host environment for graphical programming (vvvv), therefore i've deleted host specific decalarations of inputs (FType,FSenderName) and outputs (FF,FS,FP,FT) to avoid confusion. These will be used to "connect" this code with other functionality. Evaluate will be called every frame by the host.
Now to the actual question(s):
it's working fine so far in 32 bit, but in 64 bit my c# host crashes without any message. after some reading i believe this is due to pointer sizes being different in 32/64bit systems, but i'm not exactly sure what to do/if this actually applies here. I would be very thankful if you could
- explain me how (and why) to get this code to run in 64 bit
- point out any other mistakes you might spot along the way- i'm completely new to c++ and still a beginner in c#, so i'm pretty confident there's a lot to improve here; especially: memory leaks and passing the values from c++ to c# and vice versa...uiuiui.
I've understood that I shouldn't cast a pointer to an int in 64 bit, so the last thing I've tried is to change from
int Vcontrols = numControls[0];
int Tcontrols = numControls[1];
int Pcontrols = numControls[2];
int Scontrols = numControls[3];
to
int Vcontrols = (INT_PTR)numControls[0];
int Tcontrols = (INT_PTR)numControls[1];
int Pcontrols = (INT_PTR)numControls[2];
int Scontrols = (INT_PTR)numControls[3];
but with no luck, therefore I'm posting my original problem, even if this is a correct improvement(?).
EDIT: thanks to @dkackman for pointing out one unclear point: my cpp code calls functions which come as source code (SpoutControls.h) with the native 32 bit dll. It's not the source for the 32 bit dll itself but declares the functions used to (as far as i can tell) access the same shared memory as the 32 bit dll. I can also copy paste the code here if this might be the problem? Also can be found here
thank you.
回答1:
I am afraid you are out of luck. If your process is 64bit, you won't be able to load that 32bit dll, no matter how much you try.
Can I load a 32 bit DLL into a 64 bit process on Windows?
from https://msdn.microsoft.com/en-us/library/windows/desktop/aa384231(v=vs.85).aspx
On 64-bit Windows, a 64-bit process cannot load a 32-bit dynamic-link library (DLL).
Without access to its source, your only option would be to convert your host to 32bit or otherwise figure out how to host the 32bit plugin in a 32bit process and use some sort of IPC to communicate with it from a 64bit host process.
So my guess is that this has nothing to do with your wrapper, array passing or interop code.
来源:https://stackoverflow.com/questions/39192120/my-c-and-c-sharp-interop-crashes-in-64-bit-why-pointer-size